package com.sky.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.sky.constant.MessageConstant;
import com.sky.context.BaseContext;
import com.sky.dto.*;
import com.sky.entity.AddressBook;
import com.sky.entity.OrderDetail;
import com.sky.entity.Orders;
import com.sky.entity.ShoppingCart;
import com.sky.exception.AddressBookBusinessException;
import com.sky.exception.OrderBusinessException;
import com.sky.mapper.AddressBookMapper;
import com.sky.mapper.OrderDetailMapper;
import com.sky.mapper.OrderMapper;
import com.sky.mapper.ShopCartMapper;
import com.sky.result.PageResult;
import com.sky.service.OrderService;
import com.sky.vo.OrderStatisticsVO;
import com.sky.vo.OrderSubmitVO;
import com.sky.vo.OrderVO;
import com.sky.webSocket.WebSocketServer;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.web.client.RestTemplate;

import java.net.URI;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Service
public class OrderServiceImpl implements OrderService {

    @Autowired
    private OrderMapper orderMapper;

    @Autowired
    private OrderDetailMapper orderDetailMapper;

    @Autowired
    private ShopCartMapper shopCartMapper;

    @Autowired
    private AddressBookMapper addressBookMapper;

    @Autowired
    private RestTemplate restTemplate;

    @Autowired
    private WebSocketServer webSocketServer;

    @Value("${sky.shop.address}")
    private String shopAddress;

    @Value("${sky.baidu.ak}")
    private String ak;

    private static final String BASE_URL = "https://api.map.baidu.com/geocoding/v3/";

    /**
     * 用户提交订单
     * @param ordersSubmitDTO
     * @return
     */
    @Transactional
    public OrderSubmitVO submit(OrdersSubmitDTO ordersSubmitDTO) {
        // 业务异常（地址表为空，购物车表为空）
        AddressBook byId = addressBookMapper.getById(ordersSubmitDTO.getAddressBookId());
        if (byId == null) {
            // 抛出业务异常
            throw new RuntimeException(MessageConstant.ADDRESS_BOOK_IS_NULL);
        }
        Long userId = BaseContext.getCurrentId();
        ShoppingCart shoppingCart = new ShoppingCart();
        shoppingCart.setUserId(userId);
        List<ShoppingCart> list = shopCartMapper.list(shoppingCart);
        if (list == null || list.size() == 0) {
            // 抛出业务异常
            throw new RuntimeException(MessageConstant.SHOPPING_CART_IS_NULL);
        }
        // 通过调用百度地图API,判断当前位置是否在配送范围内,用户地址链接
        URI uri = URI.create(BASE_URL + "?address="
                + byId.getProvinceName()
                + byId.getCityName()
                + byId.getDistrictName()
                + byId.getDetail()
                + "&output=json&ak="
                + ak);
        // 商户地址url
        URI SHOPURI = URI.create(BASE_URL + "?address="
                + shopAddress
                + "&output=json&ak="
                + ak);
        String userData = restTemplate.getForObject(uri, String.class);
        String shopData = restTemplate.getForObject(SHOPURI, String.class);
        Boolean isOverFive = isOverFiveKm(userData, shopData);
        if (isOverFive) {
            // 抛出业务异常
            throw new OrderBusinessException(MessageConstant.OUT_OF_RANGE);
        }
        // 订单表插入一条数据
        Orders orders = new Orders();
        BeanUtils.copyProperties(ordersSubmitDTO, orders);
        orders.setOrderTime(LocalDateTime.now());
        orders.setPayStatus(Orders.UN_PAID);
        orders.setStatus(Orders.PENDING_PAYMENT);
        orders.setNumber(System.currentTimeMillis() + "");
        orders.setPhone(byId.getPhone());
        orders.setConsignee(byId.getConsignee());
        orders.setUserId(userId);
        orders.setAddress(byId.getProvinceName() + byId.getCityName() + byId.getDistrictName() + byId.getDetail());
        orderMapper.insert(orders);
        // 订单详情表插入多条数据
        // 遍历查询购物车表所返回的数据
        List<OrderDetail> orderDetails = new ArrayList<>();
        for (ShoppingCart cart : list) {
            OrderDetail orderDetail = new OrderDetail();
            BeanUtils.copyProperties(cart, orderDetail);
            orderDetail.setOrderId(orders.getId());//设置当前订单详情对应的订单ID
            orderDetails.add(orderDetail);
        }

        orderDetailMapper.insertBatch(orderDetails);
        // 清空购物车
        shopCartMapper.deleteByUserId(userId);
        // 封装返回结果
        OrderSubmitVO submitVO = OrderSubmitVO.builder()
                .id(orders.getId())
                .orderNumber(orders.getNumber())
                .orderTime(orders.getOrderTime())
                .orderAmount(orders.getAmount())
                .build();
        return submitVO;
    }


    /**
     * 支付成功后修改订单状态
     * @param ordersPaymentDTO
     */
    public void changeStatus(OrdersPaymentDTO ordersPaymentDTO) {
        // 根据订单号查询订单
        String orderNumber = ordersPaymentDTO.getOrderNumber();
        // 根据订单id更新订单的状态、订单号,status,payStatus
        Orders order = Orders.builder()
                .number(orderNumber)
                .status(Orders.TO_BE_CONFIRMED)
                .payStatus(Orders.PAID)
                .checkoutTime(LocalDateTime.now())
                .build();
        orderMapper.changeStatus(order);
        Long id = orderMapper.getByNumber(orderNumber);
        //通过websocket向客户端浏览器发送信息
        Map map = new HashMap();
        map.put("type", 1);//表示订单提醒
        map.put("orderId", id);
        map.put("content", "订单号:" + orderNumber + "已支付成功，请及时确认！");

        String jsonString = JSON.toJSONString(map);
        webSocketServer.sendToAllClient(jsonString);

    }

    /**
     * 获取历史订单
     * @param ordersPageQueryDTO
     * @return
     */
    @Transactional
    public PageResult getHistoryOrders(OrdersPageQueryDTO ordersPageQueryDTO) {
        PageHelper.startPage(ordersPageQueryDTO.getPage(), ordersPageQueryDTO.getPageSize());
        // 根据用户id查询订单表，不能查询其他用户的
        Long currentId = BaseContext.getCurrentId();
        ordersPageQueryDTO.setUserId(currentId);

        // 存储返回的数据
        List<OrderVO> list = new ArrayList<>();
        // 获取订单信息
        Page<Orders> page = orderMapper.getHistoryOrders(ordersPageQueryDTO);
        // 将返回的订单信息遍历，根据ID获取订单详情
        if(page != null && page.getTotal() > 0) {
            for (Orders orders : page) {
                OrderVO orderVO = new OrderVO();
                BeanUtils.copyProperties(orders, orderVO);
                // 根据订单id查询订单详情
                List<OrderDetail> orderDetailList = orderDetailMapper.getByOrderId(orders.getId());
                orderVO.setOrderDetailList(orderDetailList);
                list.add(orderVO);
            }
        }
        return new PageResult(page.getTotal(), list);
    }

    /**
     * 获取订单详情
     * @param id
     */
    @Transactional
    public OrderVO getOrderDetail(Long id) {
        // 根据订单id查询订单
        Orders orders = orderMapper.getById(id);
        // 根据订单id查询订单详情
        OrderVO orderVO = new OrderVO();
        BeanUtils.copyProperties(orders, orderVO);
        List<OrderDetail> orderDetails = orderDetailMapper.getByOrderId(id);
        orderVO.setOrderDetailList(orderDetails);
        return orderVO;
    }

    /**
     * 取消订单
     * @param id
     */
    public void cancelOrder(Long id) {
        // 根据订单id查询订单
        Orders orders = orderMapper.getById(id);
        // 修改订单状态
        orders.setStatus(Orders.CANCELLED);
        orderMapper.changeStatus(orders);
    }

    /**
     * 再来一单
     * @param id
     */
    @Transactional
    public void againOrder(Long id) {
       // 获取当前用户信息
        Long userId = BaseContext.getCurrentId();
        // 根据订单id查询订单详情
        List<OrderDetail> orderDetails = orderDetailMapper.getByOrderId(id);
        // 遍历订单详情，将数据插入到购物车表
        for (OrderDetail orderDetail : orderDetails) {
            ShoppingCart shoppingCart = new ShoppingCart();
            shoppingCart.setUserId(userId);
            shoppingCart.setName(orderDetail.getName());
            shoppingCart.setImage(orderDetail.getImage());
            shoppingCart.setDishId(orderDetail.getDishId());
            // 设置套餐ID
            shoppingCart.setSetmealId(orderDetail.getSetmealId());
            //设置口味
            shoppingCart.setDishFlavor(orderDetail.getDishFlavor());
            //设置数量
            shoppingCart.setNumber(orderDetail.getNumber());
            // 设置价格
            shoppingCart.setAmount(orderDetail.getAmount());
            // 设置创建时间为当前时间
            shoppingCart.setCreateTime(LocalDateTime.now());
            shopCartMapper.insert(shoppingCart);
        }
    }

    /**
     * 获取分页订单列表
     * @param ordersPageQueryDTO
     * @return
     */
    public PageResult getOrderList(OrdersPageQueryDTO ordersPageQueryDTO) {
        PageHelper.startPage(ordersPageQueryDTO.getPage(), ordersPageQueryDTO.getPageSize());
        // 获取所有订单信息
        Page<Orders> orders = orderMapper.getHistoryOrders(ordersPageQueryDTO);

        // 部分订单状态，需要额外返回订单菜品信息，将Orders转化为OrderVO
        List<OrderVO> orderVOList = getOrderVOList(orders);

        return new PageResult(orders.getTotal(), orderVOList);
    }

    /**
     * 统计订单数量
     * @return
     */
    public OrderStatisticsVO statisticsOrder() {
        // 获取所有订单信息
        Integer toBeConfirmed = orderMapper.countStatus(Orders.TO_BE_CONFIRMED);
        Integer confirmed = orderMapper.countStatus(Orders.CONFIRMED);
        Integer delivering = orderMapper.countStatus(Orders.DELIVERY_IN_PROGRESS);

        // 查询出的信息封装OrderStatisticsVO
        OrderStatisticsVO orderStatisticsVO = OrderStatisticsVO.builder()
                .toBeConfirmed(toBeConfirmed)
                .confirmed(confirmed)
                .deliveryInProgress(delivering)
                .build();
        return orderStatisticsVO;
    }

    /**
     * 根据ID修改订单状态
     */
    public void setStatus(OrdersConfirmDTO ordersConfirmDTO) {
        ordersConfirmDTO.setStatus(Orders.CONFIRMED);
        orderMapper.setStatus(ordersConfirmDTO);
    }

    /**
     * 拒单
     * @param ordersRejectDTO
     */
    public void setStatusReject(OrdersRejectionDTO ordersRejectDTO) {
        Orders orders = orderMapper.getById(ordersRejectDTO.getId());
        orders.setStatus(Orders.CANCELLED);
        orders.setRejectionReason(ordersRejectDTO.getRejectionReason());
        //设置订单状态为拒绝
        orderMapper.changeStatus(orders);
    }

    /**
     * 根据ID修改订单状态为取消
     * @param ordersCancelDTO
     */
    public void setStatusCancel(OrdersCancelDTO ordersCancelDTO) {
        Orders orders = orderMapper.getById(ordersCancelDTO.getId());
        orders.setStatus(Orders.CANCELLED);
        orders.setCancelReason(ordersCancelDTO.getCancelReason());
        orders.setCancelTime(LocalDateTime.now());
        //设置订单状态为取消
        orderMapper.changeStatus(orders);

    }

    /**
     * 订单配送
     * @param id
     */
    public void delivery(Long id) {
        Orders orders = orderMapper.getById(id);
        orders.setStatus(Orders.DELIVERY_IN_PROGRESS);
        orderMapper.changeStatus(orders);
    }

    /**
     * 订单完成
     * @param id
     */
    public void complete(Long id) {
        Orders orders = orderMapper.getById(id);
        orders.setStatus(Orders.COMPLETED);
        orderMapper.changeStatus(orders);
    }

    /**
     * 催单
     * @param id
     */
    public void reminder(Long id) {
        //根据Id获取Number
        Orders order = orderMapper.getById(id);
        //发送websocket消息去管理端
        Map map = new HashMap();
        map.put("type", 2);//表示订单提醒
        map.put("orderId", id);
        map.put("content", "订单号:" + order.getNumber() + "请及时处理");

        String jsonString = JSON.toJSONString(map);
        webSocketServer.sendToAllClient(jsonString);
    }

    private List<OrderVO> getOrderVOList(Page<Orders> page) {
        // 需要返回订单菜品信息，自定义OrderVO响应结果
        List<OrderVO> orderVOList = new ArrayList<>();

        List<Orders> ordersList = page.getResult();
        if (!CollectionUtils.isEmpty(ordersList)) {
            for (Orders orders : ordersList) {
                // 将共同字段复制到OrderVO
                OrderVO orderVO = new OrderVO();
                BeanUtils.copyProperties(orders, orderVO);
                String orderDishes = getOrderDishesStr(orders);

                // 将订单菜品信息封装到orderVO中，并添加到orderVOList
                orderVO.setOrderDishes(orderDishes);
                orderVOList.add(orderVO);
            }
        }
        return orderVOList;
    }

    /**
     * 根据订单id获取菜品信息字符串
     *
     * @param orders
     * @return
     */
    private String getOrderDishesStr(Orders orders) {
        // 查询订单菜品详情信息（订单中的菜品和数量）
        List<OrderDetail> orderDetailList = orderDetailMapper.getByOrderId(orders.getId());

        // 将每一条订单菜品信息拼接为字符串（格式：宫保鸡丁*3；）
        List<String> orderDishList = orderDetailList.stream().map(x -> {
            String orderDish = x.getName() + "*" + x.getNumber() + ";";
            return orderDish;
        }).collect(Collectors.toList());

        // 将该订单对应的所有菜品信息拼接在一起
        return String.join("", orderDishList);
    }

    /**
     * 根据传入的两对经纬度,判断距离是否超过5公里
     */
    private boolean isOverFiveKm(String userData, String shopData) {
        // 数据格式{"result":{"level":"区县","confidence":20,"location":{"lng":112.57239624162112,"lat":37.742322528644709},"precise":0,"comprehension":100},"status":0}JSONOBJECT
        JSONObject jsonObject = JSON.parseObject(userData);
        JSONObject shopObject = JSON.parseObject(shopData);
        // 获取用户经纬度
        JSONObject userLocation = jsonObject.getJSONObject("result").getJSONObject("location");
        // 获取商户经纬度
        JSONObject shopLocation = shopObject.getJSONObject("result").getJSONObject("location");
        // 计算距离
        double distance = getDistance(userLocation.getDouble("lat"), userLocation.getDouble("lng"), shopLocation.getDouble("lat"), shopLocation.getDouble("lng"));
        // 距离大于5公里返回true
        if (distance > 5) {
            return true;
        }
        return false;
    }

    /**
     * 根据经纬度计算距离
     */
    private double getDistance(double lat1, double lng1, double lat2, double lng2) {
        // 经纬度转化成弧度
        double radLat1 = Math.toRadians(lat1);
        double radLat2 = Math.toRadians(lat2);
        // 经度之差
        double a = Math.toRadians(lng1) - Math.toRadians(lng2);
        // 纬度之差
        double b = Math.toRadians(lat1) - Math.toRadians(lat2);
        // 计算两点距离的公式
        double s = 2 * Math.asin(Math.sqrt(Math.pow(Math.sin(a / 2), 2) +
                Math.cos(radLat1) * Math.cos(radLat2) * Math.pow(Math.sin(b / 2), 2)));
        // 弧长乘地球半径, 返回单位: 千米
        s = s * 6378.137;
        // 精确到小数点后两位
        s = Math.round(s * 100) / 100.0;
        return s;
    }
}
